package com.qf.student.utils2;

import com.alibaba.druid.pool.DruidDataSource;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * jabc工具类第二版本
 */
public class JDBCUtils {
    /**
     * 声明连接池对象
     */
    public static DruidDataSource dataSource=null;
    /**
     * 声明连接对象
     */
    public static Connection conn=null;
    /**
     * 声明预编译执行对象
     */
    public static PreparedStatement state=null;
    /**
     * 声明结果集对象
     */
    public static ResultSet rs=null;
    /**
     * 创建配置文件对象
     */
    public static Properties p=new Properties();
    /**
     * 加载配置文件
     */
    static{
        try {
            p.load(JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获得连接对象的方法
     * @return Connection
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public static  Connection getConnection() throws ClassNotFoundException, SQLException {
//            //1.加载驱动
//            Class.forName("com.mysql.jdbc.Driver");
//            //2.获得连接对象
//            conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/2205myschool?useSSL=false","root","123456");
        //1.加载驱动
        Class.forName(p.getProperty("driverClassName"));
        //2.获得连接对象
        conn=DriverManager.getConnection(p.getProperty("url"),p.getProperty("username"),p.getProperty("password"));
            return conn;
    }

    /**
     * 执行增加,修改,删除的方法
     * @param sql 要执行的sql语句
     * @param obs sql语句的参数,且参数按占位符的顺序传递过来
     * @return int 受影响的行数,结果
     */
    public static int update(String sql,Object...obs) throws SQLException, ClassNotFoundException {
        //调用获得连接对象的方法
        conn=getConnection();
        //4.创建预编译执行对象
        state=conn.prepareStatement(sql);
        //用执行对象调用占位符方法传参
        for (int i=0;i<obs.length;i++){
            state.setObject(i+1,obs[i]);
        }
        //5.用执行对象调用相应的方法将sql语句传到数据库去执行,并得到结果
        int result=state.executeUpdate();
        return result;
    }
    /**
     * 执行查询的方法
     * @param sql 要执行的sql语句
     * @param obs sql语句的参数,且参数按占位符的顺序传递过来
     * @return ResultSet 返回的结果集
     */
    public static ResultSet select(String sql,Object...obs) throws SQLException, ClassNotFoundException {
        //调用获得连接对象的方法
        conn=getConnection();
        //4.创建预编译执行对象
        state=conn.prepareStatement(sql);
        //用执行对象调用占位符方法传参
        for (int i=0;i<obs.length;i++){
            state.setObject(i+1,obs[i]);
        }
        //5.用执行对象调用相应的方法将sql语句传到数据库去执行,并得到结果
        rs=state.executeQuery();
        return rs;
    }

    /**
     * 用对象接收查询的一条数据
     * 前提条件:查询的列名或列别名一定要与接收的对象的属性名一致.
     * @param clazz 对象所在类对象
     * @param sql 要执行查询语句
     * @param obs sql语句参数
     * @param <T> 泛型,接收结果类型
     * @return T
     */
    public static<T> T selectOne(Class<T> clazz,String sql,Object...obs) throws Exception{


        //调用查询工具得到结果集
        rs=select(sql,obs);
        //获得查询结果集所有的列
        ResultSetMetaData rsmd=rs.getMetaData();
        //获得结果集中总列数
        int count=rsmd.getColumnCount();
        //遍历结果集
        while(rs.next()){
            //获得结果对象的实例对象
            T t=clazz.newInstance();
            //获得当前行数据
            //获得所有的列名
            for (int i=1;i<=count;i++){
                //通过列索引获得当前列名
                String columnName=rsmd.getColumnName(i);
                //通过结果集获得当前行的当列的列值
                Object columnValue=rs.getObject(columnName);

                //因为列名与对象的属性名相同(列名就是对象的属性名),通过属性名获得属性的反射对象
                Field ff=clazz.getDeclaredField(columnName);
                //设置属性访问权限
                ff.setAccessible(true);
                //通过属性的反射对象获得属性的数据类型
                String type=ff.getGenericType().toString();

                if (type.endsWith("String")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,columnValue.toString());
                }else if(type.endsWith("Integer")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Integer.valueOf(columnValue.toString()));
                }else if(type.endsWith("Long")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Long.valueOf(columnValue.toString()));
                }else if(type.endsWith("Float")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Float.valueOf(columnValue.toString()));
                }else if(type.endsWith("Double")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Double.valueOf(columnValue.toString()));
                }else if(type.endsWith("Character")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Character.valueOf(columnValue.toString().charAt(0)));
                }else if(type.endsWith("Date")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,new SimpleDateFormat("yyyy-MM-dd").parse(columnValue.toString()));
                }

            }
            return t;
        }
        return null;
    }
    /**
     * 用对象接收查询的多条数据
     * 前提条件:查询的列名或列别名一定要与接收的对象的属性名一致.
     * @param clazz 对象所在类对象
     * @param sql 要执行查询语句
     * @param obs sql语句参数
     * @param <T> 泛型,接收结果类型
     * @return T
     */
    public static<T> List<T> selectList(Class<T> clazz, String sql, Object...obs) throws Exception{
        //创建集合存结果
        List<T> tList=new ArrayList<>();

        //调用查询工具得到结果集
        rs=select(sql,obs);
        //获得查询结果集所有的列
        ResultSetMetaData rsmd=rs.getMetaData();
        //获得结果集中总列数
        int count=rsmd.getColumnCount();
        //遍历结果集
        while(rs.next()){
            //获得结果对象的实例对象
            T t=clazz.newInstance();

            //获得当前行数据
            //获得所有的列名(getColumnName第一列是1,第二列2...)
            for (int i=1;i<=count;i++){
                //通过列索引获得当前列名
                String columnName=rsmd.getColumnName(i);
                //通过结果集获得当前行的当列的列值
                Object columnValue=rs.getObject(columnName);

                //因为列名与对象的属性名相同(列名就是对象的属性名),通过属性名获得属性的反射对象
                Field ff=clazz.getDeclaredField(columnName);
                //设置属性访问权限
                ff.setAccessible(true);
                //通过属性的反射对象获得属性的数据类型
                String type=ff.getGenericType().toString();

                if (type.endsWith("String")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,columnValue.toString());
                }else if(type.endsWith("Integer")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Integer.valueOf(columnValue.toString()));
                }else if(type.endsWith("Long")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Long.valueOf(columnValue.toString()));
                }else if(type.endsWith("Float")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Float.valueOf(columnValue.toString()));
                }else if(type.endsWith("Double")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,Double.valueOf(columnValue.toString()));
                }else if(type.endsWith("Character")){
                    //通过属性的反射对象将属性值设置到指定的对象的属性中
                    ff.set(t,columnValue.toString().charAt(0));
                }else if (type.endsWith("Date")){
                    //通过属性的反射对象将属性值设置到指定对象的属性中
                    ff.set(t, new SimpleDateFormat("yyyy-MM-dd").parse(columnValue.toString()));
                }

            }
            //将对象添加到集合中
            tList.add(t);
        }
        return tList;
    }
//    /**
//     * 关闭jdbc常用对象
//     * @throws SQLException
//     */
//    public static void closeObject() throws SQLException {
//        if(rs!=null){
//            rs.close();
//        }if(state!=null){
//            state.close();
//        }if(conn!=null){
//            conn.close();
//        }
//    }
    /**
     *  万能关闭对象的方法,可关闭所有可关闭的对象
     */
    public static void closeObject2(AutoCloseable...obs) throws Exception {
        for(AutoCloseable ob:obs){
            if(ob!=null){
                ob.close();
            }
        }
    }
}
